/**
 * $Id: Menus.js,v 1.16 2013/05/07 09:48:56 gaudenz Exp $
 * Copyright (c) 2006-2012, JGraph Ltd
 */
/**
 * Constructs a new graph editor
 */
Menus = function(editorUi)
{
    this.editorUi = editorUi;
    this.menus = new Object();
    this.init();

    // Pre-fetches checkmark image
    new Image().src = IMAGE_PATH + '/checkmark.gif';
};

/**
 * Adds the label menu items to the given menu and parent.
 */
Menus.prototype.init = function()
{
    var graph = this.editorUi.editor.graph;

    this.put('fontFamily', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        var fonts = ['Helvetica', 'Verdana', 'Times New Roman', 'Garamond', 'Comic Sans MS',
            'Courier New', 'Georgia', 'Lucida Console', 'Tahoma'];

        for (var i = 0; i < fonts.length; i++)
        {
            var tr = this.styleChange(menu, fonts[i], [mxConstants.STYLE_FONTFAMILY], [fonts[i]], null, parent);
            tr.firstChild.nextSibling.style.fontFamily = fonts[i];
        }

        menu.addSeparator(parent);
        this.promptChange(menu, mxResources.get('custom'), '', mxConstants.DEFAULT_FONTFAMILY, mxConstants.STYLE_FONTFAMILY, parent);
    })));
    this.put('fontSize', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        var sizes = [6, 8, 9, 10, 11, 12, 14, 18, 24, 36, 48, 72];

        for (var i = 0; i < sizes.length; i++)
        {
            this.styleChange(menu, sizes[i], [mxConstants.STYLE_FONTSIZE], [sizes[i]], null, parent);
        }

        menu.addSeparator(parent);
        this.promptChange(menu, mxResources.get('custom'), '(pt)', '12', mxConstants.STYLE_FONTSIZE, parent);
    })));
    this.put('linewidth', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        var sizes = [1, 2, 3, 4, 8, 12, 16, 24];

        for (var i = 0; i < sizes.length; i++)
        {
            this.styleChange(menu, sizes[i] + 'px', [mxConstants.STYLE_STROKEWIDTH], [sizes[i]], null, parent);
        }

        menu.addSeparator(parent);
        this.promptChange(menu, mxResources.get('custom'), '(px)', '1', mxConstants.STYLE_STROKEWIDTH, parent);
    })));
    this.put('line', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.styleChange(menu, mxResources.get('straight'), [mxConstants.STYLE_EDGE], [null], null, parent);
        this.styleChange(menu, mxResources.get('entityRelation'), [mxConstants.STYLE_EDGE], ['entityRelationEdgeStyle'], null, parent);
        menu.addSeparator(parent);
        this.styleChange(menu, mxResources.get('horizontal'), [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW], ['elbowEdgeStyle', 'horizontal'], null, parent);
        this.styleChange(menu, mxResources.get('vertical'), [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW], ['elbowEdgeStyle', 'vertical'], null, parent);
        menu.addSeparator(parent);
        this.styleChange(menu, mxResources.get('manual'), [mxConstants.STYLE_EDGE], ['segmentEdgeStyle'], null, parent);
        this.styleChange(menu, mxResources.get('automatic'), [mxConstants.STYLE_EDGE], ['orthogonalEdgeStyle'], null, parent);
    })));
    this.put('lineend', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.styleChange(menu, mxResources.get('classic'), [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_CLASSIC], null, parent);
        this.styleChange(menu, mxResources.get('openArrow'), [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_OPEN], null, parent);
        this.styleChange(menu, mxResources.get('block'), [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_BLOCK], null, parent);
        menu.addSeparator(parent);
        this.styleChange(menu, mxResources.get('oval'), [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_OVAL], null, parent);
        this.styleChange(menu, mxResources.get('diamond'), [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_DIAMOND], null, parent);
        this.styleChange(menu, mxResources.get('diamondThin'), [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_DIAMOND_THIN], null, parent);
        menu.addSeparator(parent);
        this.styleChange(menu, mxResources.get('none'), [mxConstants.STYLE_ENDARROW], [mxConstants.NONE], null, parent);
        menu.addSeparator(parent);
        menu.addItem(mxResources.get('transparent'), null, function() {
            graph.toggleCellStyles('endFill', true);
        }, parent, null, true);
        menu.addSeparator(parent);
        this.promptChange(menu, mxResources.get('size'), '(px)', mxConstants.DEFAULT_MARKERSIZE, mxConstants.STYLE_ENDSIZE, parent);
    })));
    this.put('linestart', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.styleChange(menu, mxResources.get('classic'), [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_CLASSIC], null, parent);
        this.styleChange(menu, mxResources.get('openArrow'), [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_OPEN], null, parent);
        this.styleChange(menu, mxResources.get('block'), [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_BLOCK], null, parent);
        menu.addSeparator(parent);
        this.styleChange(menu, mxResources.get('oval'), [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_OVAL], null, parent);
        this.styleChange(menu, mxResources.get('diamond'), [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_DIAMOND], null, parent);
        this.styleChange(menu, mxResources.get('diamondThin'), [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_DIAMOND_THIN], null, parent);
        menu.addSeparator(parent);
        this.styleChange(menu, mxResources.get('none'), [mxConstants.STYLE_STARTARROW], [mxConstants.NONE], null, parent);
        menu.addSeparator(parent);
        menu.addItem(mxResources.get('transparent'), null, function() {
            graph.toggleCellStyles('startFill', true);
        }, parent, null, true);
        menu.addSeparator(parent);
        this.promptChange(menu, mxResources.get('size'), '(px)', mxConstants.DEFAULT_MARKERSIZE, mxConstants.STYLE_STARTSIZE, parent);
    })));
    this.put('spacing', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        // Uses shadow action and line menu to analyze selection
        var vertexSelected = this.editorUi.actions.get('shadow').enabled;
        var edgeSelected = this.get('line').enabled;

        if (vertexSelected || menu.showDisabled)
        {
            this.promptChange(menu, mxResources.get('top'), '(px)', '0', mxConstants.STYLE_SPACING_TOP, parent, vertexSelected);
            this.promptChange(menu, mxResources.get('right'), '(px)', '0', mxConstants.STYLE_SPACING_RIGHT, parent, vertexSelected);
            this.promptChange(menu, mxResources.get('bottom'), '(px)', '0', mxConstants.STYLE_SPACING_BOTTOM, parent, vertexSelected);
            this.promptChange(menu, mxResources.get('left'), '(px)', '0', mxConstants.STYLE_SPACING_LEFT, parent, vertexSelected);
            menu.addSeparator(parent);
            this.promptChange(menu, mxResources.get('global'), '(px)', '0', mxConstants.STYLE_SPACING, parent, vertexSelected);
            this.promptChange(menu, mxResources.get('perimeter'), '(px)', '0', mxConstants.STYLE_PERIMETER_SPACING, parent, vertexSelected);
        }

        if (edgeSelected || menu.showDisabled)
        {
            menu.addSeparator(parent);
            this.promptChange(menu, mxResources.get('sourceSpacing'), '(px)', '0', mxConstants.STYLE_SOURCE_PERIMETER_SPACING, parent, edgeSelected);
            this.promptChange(menu, mxResources.get('targetSpacing'), '(px)', '0', mxConstants.STYLE_TARGET_PERIMETER_SPACING, parent, edgeSelected);
        }
    })));
    this.put('format', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.addMenuItems(menu, ['fillColor'], parent);
        this.addSubmenu('gradient', menu, parent);
        this.addMenuItems(menu, ['-', 'shadow'], parent);
        this.promptChange(menu, mxResources.get('opacity'), '(%)', '100', mxConstants.STYLE_OPACITY, parent, this.get('format').enabled);
      
        
        this.addMenuItems(menu, ['-', 'curved', 'rounded', 'dashed', '-', 'strokeColor'], parent);
        this.addSubmenu('linewidth', menu, parent);
        this.addMenuItems(menu, ['-'], parent);
        this.addSubmenu('line', menu, parent);
        this.addMenuItems(menu, ['-'], parent);
        this.addSubmenu('linestart', menu, parent);
        this.addSubmenu('lineend', menu, parent);
        menu.addSeparator(parent);
        this.addMenuItem(menu, 'style', parent);
    })));
    this.put('gradient', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.addMenuItems(menu, ['gradientColor', '-'], parent);
        this.styleChange(menu, mxResources.get('north'), [mxConstants.STYLE_GRADIENT_DIRECTION], [mxConstants.DIRECTION_NORTH], null, parent);
        this.styleChange(menu, mxResources.get('east'), [mxConstants.STYLE_GRADIENT_DIRECTION], [mxConstants.DIRECTION_EAST], null, parent);
        this.styleChange(menu, mxResources.get('south'), [mxConstants.STYLE_GRADIENT_DIRECTION], [mxConstants.DIRECTION_SOUTH], null, parent);
        this.styleChange(menu, mxResources.get('west'), [mxConstants.STYLE_GRADIENT_DIRECTION], [mxConstants.DIRECTION_WEST], null, parent);
    })));
    this.put('text', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        var enabled = this.get('text').enabled;
        menu.addSeparator(parent);
        this.addMenuItem(menu, 'fontColor', parent);
        this.addMenuItems(menu, ['backgroundColor', 'borderColor', '-'], parent);
        this.addSubmenu('fontFamily', menu, parent);
        this.addSubmenu('fontSize', menu, parent);
        this.addMenuItems(menu, ['-', 'bold', 'italic', 'underline', '-'], parent);
        this.addSubmenu('alignment', menu, parent);
        this.addMenuItem(menu, 'wordWrap', parent);
        menu.addSeparator(parent);
        this.promptChange(menu, mxResources.get('textOpacity'), '(%)', '100', mxConstants.STYLE_TEXT_OPACITY, parent, enabled);
        menu.addItem(mxResources.get('hide'), null, function() {
            graph.toggleCellStyles(mxConstants.STYLE_NOLABEL, false);
        }, parent, null, enabled);
    })));
    this.put('alignment', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.styleChange(menu, mxResources.get('leftAlign'), [mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_LEFT], null, parent);
        this.styleChange(menu, mxResources.get('center'), [mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_CENTER], null, parent);
        this.styleChange(menu, mxResources.get('rightAlign'), [mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_RIGHT], null, parent);
        menu.addSeparator(parent);
        this.styleChange(menu, mxResources.get('topAlign'), [mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_TOP], null, parent);
        this.styleChange(menu, mxResources.get('middle'), [mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_MIDDLE], null, parent);
        this.styleChange(menu, mxResources.get('bottomAlign'), [mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_BOTTOM], null, parent);
        menu.addSeparator(parent);
        this.addSubmenu('position', menu, parent);
        this.addSubmenu('spacing', menu, parent);
        menu.addSeparator(parent);
        var enabled = this.get('text').enabled;
        menu.addItem(mxResources.get('vertical'), null, function() {
            graph.toggleCellStyles(mxConstants.STYLE_HORIZONTAL, true);
        }, parent, null, enabled);
    })));
    this.put('position', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.styleChange(menu, mxResources.get('left'), [mxConstants.STYLE_LABEL_POSITION, mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_RIGHT], null, parent);
        this.styleChange(menu, mxResources.get('center'), [mxConstants.STYLE_LABEL_POSITION, mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_CENTER], null, parent);
        this.styleChange(menu, mxResources.get('right'), [mxConstants.STYLE_LABEL_POSITION, mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_LEFT], null, parent);
        menu.addSeparator(parent);
        this.styleChange(menu, mxResources.get('top'), [mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_TOP, mxConstants.ALIGN_BOTTOM], null, parent);
        this.styleChange(menu, mxResources.get('middle'), [mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_MIDDLE], null, parent);
        this.styleChange(menu, mxResources.get('bottom'), [mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_TOP], null, parent);
    })));
    this.put('direction', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        menu.addItem(mxResources.get('flipH'), null, function() {
            graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false);
        }, parent);
        menu.addItem(mxResources.get('flipV'), null, function() {
            graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false);
        }, parent);
        this.addMenuItems(menu, ['-', 'tilt', 'rotation'], parent);
    })));
    this.put('align', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        menu.addItem(mxResources.get('leftAlign'), null, function() {
            graph.alignCells(mxConstants.ALIGN_LEFT);
        }, parent);
        menu.addItem(mxResources.get('center'), null, function() {
            graph.alignCells(mxConstants.ALIGN_CENTER);
        }, parent);
        menu.addItem(mxResources.get('rightAlign'), null, function() {
            graph.alignCells(mxConstants.ALIGN_RIGHT);
        }, parent);
        menu.addSeparator(parent);
        menu.addItem(mxResources.get('topAlign'), null, function() {
            graph.alignCells(mxConstants.ALIGN_TOP);
        }, parent);
        menu.addItem(mxResources.get('middle'), null, function() {
            graph.alignCells(mxConstants.ALIGN_MIDDLE);
        }, parent);
        menu.addItem(mxResources.get('bottomAlign'), null, function() {
            graph.alignCells(mxConstants.ALIGN_BOTTOM);
        }, parent);
    })));
    this.put('layers', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        var p = graph.getDefaultParent();
        var selectedLayer = mxResources.get('background');

        var item = menu.addItem(selectedLayer, null, mxUtils.bind(this, function()
        {
            graph.setDefaultParent(null);
        }), parent);

        if (p == graph.model.getChildAt(graph.model.root, 0))
        {
            this.addCheckmark(item);
        }

        var layerCount = graph.model.getChildCount(graph.model.root);

        for (var i = 1; i < layerCount; i++)
        {
            (mxUtils.bind(this, function(child)
            {
                var title = child.value || mxResources.get('layer') + ' ' + i;

                if (!graph.model.isVisible(child))
                {
                    title += ' (' + mxResources.get('hidden') + ')';
                }
                var item = menu.addItem(title, null, function()
                {
                    if (!graph.model.isVisible(child))
                    {
                        graph.model.setVisible(child, true);

                        // Forces a complete refresh to hide the edges in other
                        // layers which are connected to children of this layer
                        graph.view.invalidate();
                    }

                    graph.setDefaultParent(child);
                }, parent);

                if (p == child)
                {
                    this.addCheckmark(item);
                    selectedLayer = title;
                }
            }))(graph.model.getChildAt(graph.model.root, i));
        }

        var notBackground = p != graph.model.getChildAt(graph.model.root, 0);
        menu.addSeparator(parent);

        menu.addItem(mxResources.get('removeIt', [selectedLayer]), null, mxUtils.bind(this, function()
        {
            graph.removeCells([p]);
            graph.setDefaultParent(null);
        }), parent, null, notBackground);

        menu.addItem(mxResources.get('renameIt', [selectedLayer]), null, mxUtils.bind(this, function()
        {
            var newName = mxUtils.prompt(mxResources.get('enterName'), selectedLayer);

            if (newName != null && newName.length > 0)
            {
                graph.getModel().setValue(p, newName);
            }
        }), parent, null, notBackground);

        menu.addItem(mxResources.get('hideIt', [selectedLayer]), null, mxUtils.bind(this, function()
        {
            if (graph.model.isVisible(p))
            {
                graph.model.beginUpdate();
                try
                {
                    graph.model.setVisible(p, !graph.model.isVisible(p));

                    // Forces a complete refresh to hide the edges in other
                    // layers which are connected to children of this layer
                    graph.view.invalidate();
                }
                finally
                {
                    graph.model.endUpdate();
                }

                graph.setDefaultParent(null);
            }
        }), parent, null, notBackground);

        menu.addSeparator(parent);

        menu.addItem(mxResources.get('moveSelectionTo', [selectedLayer]), null, mxUtils.bind(this, function()
        {
            graph.moveCells(graph.getSelectionCells(), 0, 0, false, p);
        }), parent, null, !graph.isSelectionEmpty());

        menu.addSeparator(parent);

        menu.addItem(mxResources.get('addLayer'), null, mxUtils.bind(this, function()
        {
            var cell = graph.addCell(new mxCell(), graph.model.root);
            graph.setDefaultParent(cell);
        }), parent);
    })));
    this.put('layout', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        menu.addItem(mxResources.get('horizontalTree'), null, mxUtils.bind(this, function()
        {
            if (!graph.isSelectionEmpty())
            {
                var layout = new mxCompactTreeLayout(graph, true);
                layout.edgeRouting = false;
                layout.levelDistance = 30;
                this.editorUi.executeLayout(layout, true, true);
            }
        }), parent);
        menu.addItem(mxResources.get('verticalTree'), null, mxUtils.bind(this, function()
        {
            if (!graph.isSelectionEmpty())
            {
                var layout = new mxCompactTreeLayout(graph, false);
                layout.edgeRouting = false;
                layout.levelDistance = 30;
                this.editorUi.executeLayout(layout, true, true);
            }
        }), parent);
        menu.addSeparator(parent);
        menu.addItem(mxResources.get('horizontalFlow'), null, mxUtils.bind(this, function()
        {
            var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_WEST);
            this.editorUi.executeLayout(layout, true, true);
        }), parent);
        menu.addItem(mxResources.get('verticalFlow'), null, mxUtils.bind(this, function()
        {
            var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_NORTH);
            this.editorUi.executeLayout(layout, true, true);
        }), parent);
        menu.addSeparator(parent);
        menu.addItem(mxResources.get('organic'), null, mxUtils.bind(this, function()
        {
            var layout = new mxFastOrganicLayout(graph);
            this.editorUi.executeLayout(layout, true, true);
        }), parent);
        menu.addItem(mxResources.get('circle'), null, mxUtils.bind(this, function()
        {
            var layout = new mxCircleLayout(graph);
            this.editorUi.executeLayout(layout, true, true, graph.getSelectionCells());
        }), parent);
    })));
    this.put('navigation', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.addMenuItems(menu, ['home', '-', 'exitGroup', 'enterGroup', '-', 'expand', 'collapse'], parent);
    })));
    this.put('arrange', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.addMenuItems(menu, ['toFront', 'toBack', '-'], parent);
        this.addSubmenu('direction', menu, parent);
        this.addSubmenu('layout', menu, parent);
        this.addSubmenu('align', menu, parent);
        menu.addSeparator(parent);
        this.addSubmenu('layers', menu, parent);
        this.addSubmenu('navigation', menu, parent);
        this.addMenuItems(menu, ['-', 'group', 'ungroup', 'removeFromGroup', '-', 'lockUnlock', '-', 'autosize'], parent);
    })));
    this.put('view', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.addMenuItems(menu, ['actualSize'], parent);
        menu.addSeparator();
        var scales = [0.25, 0.5, 0.75, 1, 1.5, 2, 4];

        for (var i = 0; i < scales.length; i++)
        {
            (function(scale)
            {
                menu.addItem((scale * 100) + '%', null, function()
                {
                    graph.zoomTo(scale);
                }, parent);
            })(scales[i]);
        }

        this.addMenuItems(menu, ['-', 'zoomIn', 'zoomOut', '-', 'fitWindow', 'customZoom', '-', 'fitPage', 'fitPageWidth'], parent);
    })));
    this.put('file', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.addMenuItems(menu, ['new', 'open', '-', 'save', 'saveAs', '-', 'import', 'export', '-', 'editFile', '-', 'pageSetup', 'print'], parent);
    })));
    this.put('edit', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        
        
        
        this.addMenuItems(menu, ['undo', 'redo', '-', 'cut', 'copy', 'paste', 'opaste', 'delete', '-', 'duplicate', '-',
            'editLink', '-',
            'selectVertices', 'selectEdges', 'selectAll', '-', 'setAsDefaultEdge']);
    })));
    this.put('options', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.addMenuItems(menu, ['grid', 'guides', 'tooltips', '-', 'connect', 'copyConnect', 'navigation',
            '-', 'scrollbars', 'pageView', '-', 'pageBackgroundColor']);
    })));
    this.put('help', new Menu(mxUtils.bind(this, function(menu, parent)
    {
        this.addMenuItems(menu, ['help', '-', 'about']);
    })));
};

/**
 * Adds the label menu items to the given menu and parent.
 */
Menus.prototype.put = function(name, menu)
{
    this.menus[name] = menu;
};

/**
 * Adds the label menu items to the given menu and parent.
 */
Menus.prototype.get = function(name)
{
    return this.menus[name];
};

/**
 * Adds the given submenu.
 */
Menus.prototype.addSubmenu = function(name, menu, parent)
{
    var enabled = this.get(name).enabled;

    if (menu.showDisabled || enabled)
    {
        var submenu = menu.addItem(mxResources.get(name), null, null, parent, null, enabled);
        this.addMenu(name, menu, submenu);
    }
};

/**
 * Adds the label menu items to the given menu and parent.
 */
Menus.prototype.addMenu = function(name, popupMenu, parent)
{
    var menu = this.get(name);

    if (menu != null && (popupMenu.showDisabled || menu.enabled))
    {
        this.get(name).execute(popupMenu, parent);
    }
};

/**
 * Adds a style change item to the given menu.
 */
Menus.prototype.styleChange = function(menu, label, keys, values, sprite, parent)
{
    return menu.addItem(label, null, mxUtils.bind(this, function()
    {
        var graph = this.editorUi.editor.graph;

        graph.getModel().beginUpdate();
        try
        {
            for (var i = 0; i < keys.length; i++)
            {
                graph.setCellStyles(keys[i], values[i]);
            }
        }
        finally
        {
            graph.getModel().endUpdate();
        }
    }), parent, sprite);
};

/**
 * Adds a style change item with a prompt to the given menu.
 */
Menus.prototype.promptChange = function(menu, label, hint, defaultValue, key, parent, enabled)
{
    return menu.addItem(label, null, mxUtils.bind(this, function()
    {
        var graph = this.editorUi.editor.graph;
        var value = defaultValue;
        var state = graph.getView().getState(graph.getSelectionCell());

        if (state != null)
        {
            value = state.style[key] || value;
        }

        value = mxUtils.prompt(mxResources.get('enterValue') + ((hint.length > 0) ? (' ' + hint) : ''), value);

        if (value != null && value.length > 0)
        {
            graph.setCellStyles(key, value);
        }
    }), parent, null, enabled);
};

/**
 * Adds a handler for showing a menu in the given element.
 */
Menus.prototype.pickColor = function(key)
{
    if (this.colorDialog == null)
    {
        this.colorDialog = new ColorDialog(this.editorUi);
    }

    this.colorDialog.currentColorKey = key;
    var graph = this.editorUi.editor.graph;
    var state = graph.getView().getState(graph.getSelectionCell());
    var color = 'none';

    if (state != null)
    {
        color = state.style[key] || color;
    }

    if (color == 'none')
    {
        color = 'ffffff';
        this.colorDialog.picker.fromString('ffffff');
        this.colorDialog.colorInput.value = 'none';
    }
    else
    {
        this.colorDialog.picker.fromString(color);
    }

    this.editorUi.showDialog(this.colorDialog.container, 220, 400, true, false);

    if (!mxClient.IS_TOUCH)
    {
        this.colorDialog.colorInput.focus();
    }
};

/**
 * Creates the keyboard event handler for the current graph and history.
 */
Menus.prototype.addMenuItem = function(menu, key, parent)
{
    var action = this.editorUi.actions.get(key);




    if (action != null && (menu.showDisabled || action.enabled))
    {
        var item = menu.addItem(action.label, null, action.funct, parent, null, action.enabled);

        // Adds checkmark image
        if (action.toggleAction && action.isSelected())
        {
            this.addCheckmark(item);
        }

        this.addShortcut(item, action);

        return item;
    }

    return null;
};

/**
 * Adds a checkmark to the given menuitem.
 */
Menus.prototype.addShortcut = function(item, action)
{
    if (action.shortcut != null)
    {
        var td = item.firstChild.nextSibling.nextSibling;
        var span = document.createElement('span');
        span.style.color = 'gray';
        mxUtils.write(span, action.shortcut);
        td.appendChild(span);
    }
};

/**
 * Adds a checkmark to the given menuitem.
 */
Menus.prototype.addCheckmark = function(item)
{
    var td = item.firstChild.nextSibling;
    td.style.backgroundImage = 'url(' + IMAGE_PATH + '/checkmark.gif)';
    td.style.backgroundRepeat = 'no-repeat';
    td.style.backgroundPosition = '2px 50%';
};

/**
 * Creates the keyboard event handler for the current graph and history.
 */
Menus.prototype.addMenuItems = function(menu, keys, parent)
{
    for (var i = 0; i < keys.length; i++)
    {
        if (keys[i] == '-')
        {
            menu.addSeparator(parent);
        }
        else
        {
            this.addMenuItem(menu, keys[i], parent);
        }
    }
};

/**
 * Creates the keyboard event handler for the current graph and history.
 */
Menus.prototype.createPopupMenu = function(menu, cell, evt)
{
    var graph = this.editorUi.editor.graph;
    menu.smartSeparators = true;

    if (graph.isSelectionEmpty())
    {
        this.addMenuItems(menu, ['undo', 'redo', '-', 'paste', 'opaste', '-']);
    }
    else
    {
        this.addMenuItems(menu, ['delete', '-', 'cut', 'copy', '-', 'duplicate']);

        if (graph.getSelectionCount() == 1 && graph.getModel().isEdge(graph.getSelectionCell()))
        {
            this.addMenuItems(menu, ['setAsDefaultEdge']);
        }

        menu.addSeparator();
    }

    if (graph.getSelectionCount() > 0)
    {





        var cells = graph.getSelectionCells();
        var thisCell = cells[0];

        if (this.editorUi.editor.isSwimHorizontal(thisCell)) {

            this.addMenuItems(menu, [ '-', 'toLeft', 'toRight', '-', 'addtop', 'addbelow', 'addtoFirst', 'addtoLast', 'addChild', '-']);



        } else if (this.editorUi.editor.isSwimLane(thisCell)) {

            this.addMenuItems(menu, [ '-', 'toFirst', 'toLast', 'toLeft', 'toRight', '-', 'addtoLeft', 'addtoRight', 'addtoFirst', 'addtoLast', 'addChild', '-']);

        } else {

            this.addMenuItems(menu, ['toFront', 'toBack']);



        }






        if (graph.getModel().isEdge(graph.getSelectionCell()))
        {
            var isWaypoint = false;
            var cell = graph.getSelectionCell();

            if (cell != null && graph.getModel().isEdge(cell))
            {
                var handler = graph.selectionCellsHandler.getHandler(cell);

                if (handler instanceof mxEdgeHandler && handler.bends != null && handler.bends.length > 2)
                {
                    var index = handler.getHandleForEvent(new mxMouseEvent(evt));

                    // Configures removeWaypoint action before execution
                    var rmWaypointAction = this.editorUi.actions.get('removeWaypoint');
                    rmWaypointAction.handler = handler;
                    rmWaypointAction.index = index;

                    isWaypoint = index > 0 && index < handler.bends.length;
                }
            }

            this.addMenuItems(menu, ['-', (isWaypoint) ? 'removeWaypoint' : 'addWaypoint']);
        }
        else if (graph.getSelectionCount() > 1)
        {
            menu.addSeparator();
            this.addMenuItems(menu, ['group']);
        }

        menu.addSeparator();

        if (graph.getSelectionCount() == 1)
        {
            var link = graph.getLinkForCell(graph.getSelectionCell());

            if (link != null)
            {
                this.addMenuItems(menu, ['openLink']);
            }
        }
    }
    else
    {
        this.addMenuItems(menu, ['-', 'selectVertices', 'selectEdges', '-', 'selectAll']);
    }
}
;

/**
 * Creates the keyboard event handler for the current graph and history.
 */
Menus.prototype.createMenubar = function(container)
{
    var menubar = new Menubar(this.editorUi, container);
    var menus = ['file', 'edit', 'view', 'format', 'text', 'arrange', 'options', 'help'];

    for (var i = 0; i < menus.length; i++)
    {
        menubar.addMenu(mxResources.get(menus[i]), this.get(menus[i]).funct);
    }

    return menubar;
};

/**
 * Construcs a new menubar for the given editor.
 */
function Menubar(editorUi, container)
{
    this.editorUi = editorUi;
    this.container = container;

    // Global handler to hide the current menu
    mxEvent.addGestureListeners(document, mxUtils.bind(this, function(evt)
    {
        this.hideMenu();
    }));
}
;

/**
 * Adds the menubar elements.
 */
Menubar.prototype.hideMenu = function()
{
    if (this.currentMenu != null)
    {
        this.currentMenu.hideMenu();
    }
};

/**
 * Adds a submenu to this menubar.
 */
Menubar.prototype.addMenu = function(label, funct)
{
    var elt = document.createElement('a');
    elt.setAttribute('href', 'javascript:void(0);');
    elt.className = 'geItem';
    mxUtils.write(elt, label);

    this.addMenuHandler(elt, funct);
    this.container.appendChild(elt);

    return elt;
};

/**
 * Adds a handler for showing a menu in the given element.
 */
Menubar.prototype.addMenuHandler = function(elt, funct)
{
    if (funct != null)
    {
        var show = true;

        var clickHandler = mxUtils.bind(this, function(evt)
        {
            if (show && elt.enabled == null || elt.enabled)
            {
              	this.editorUi.editor.graph.popupMenuHandler.hideMenu();
                
                //this.editorUi.editor.graph.popupMenuHandler.hideMenu();
                var menu = new mxPopupMenu(funct);
                menu.div.className += ' geMenubarMenu';
                menu.smartSeparators = true;
                menu.showDisabled = true;
                menu.autoExpand = true;

                // Disables autoexpand and destroys menu when hidden
                menu.hideMenu = mxUtils.bind(this, function()
                {
                    mxPopupMenu.prototype.hideMenu.apply(menu, arguments);
                    menu.destroy();
                    this.currentMenu = null;
                    this.currentElt = null;
                });

                menu.popup(elt.offsetLeft + 4, elt.offsetTop + elt.offsetHeight + 4, null, evt);
                this.currentMenu = menu;
                this.currentElt = elt;
            }

            show = true;
            mxEvent.consume(evt);
        });

        // Shows menu automatically while in expanded state
        mxEvent.addListener(elt, 'mousemove', mxUtils.bind(this, function(evt)
        {
            if (this.currentMenu != null && this.currentElt != elt)
            {
                this.hideMenu();
                clickHandler(evt);
            }
        }));

        // Hides menu if already showing
        mxEvent.addListener(elt, 'mousedown', mxUtils.bind(this, function()
        {
            show = this.currentElt != elt;
        }));

        mxEvent.addListener(elt, 'click', clickHandler);
    }
};

/**
 * Constructs a new action for the given parameters.
 */
function Menu(funct, enabled)
{
    mxEventSource.call(this);
    this.funct = funct;
    this.enabled = (enabled != null) ? enabled : true;
}
;

// Menu inherits from mxEventSource
mxUtils.extend(Menu, mxEventSource);

/**
 * Sets the enabled state of the action and fires a stateChanged event.
 */
Menu.prototype.setEnabled = function(value)
{
    if (this.enabled != value)
    {
        this.enabled = value;
        this.fireEvent(new mxEventObject('stateChanged'));
    }
};

/**
 * Sets the enabled state of the action and fires a stateChanged event.
 */
Menu.prototype.execute = function(menu, parent)
{
    this.funct(menu, parent);
};
